package server

import (
	"context"
	"errors"
	"fmt"
	"go.uber.org/zap"
	"golang.org/x/net/ipv4"
	"net"
	"onvif_server/internal/service"
	"onvif_server/pkg/network"
)

type DiscoveryConfig struct {
	Multicast string `help:"组播地址" default:"239.255.255.250"`
	Port      uint   `help:"组播端口" default:"7659"`
}

type MsgHandle func([]byte, net.Addr) ([]byte, error)

type msgChan struct {
	msg []byte
	cm  *ipv4.ControlMessage
	src net.Addr
}

type Discovery struct {
	config     *DiscoveryConfig
	log        *zap.Logger
	packetConn *ipv4.PacketConn
	ifcs       []network.NetInterface
	groupAddr  *net.UDPAddr
	msgHandle  MsgHandle

	msgChan chan msgChan
}

func NewDiscovery(
	config *DiscoveryConfig,
	log *zap.Logger,
	settingService service.SettingService) (*Discovery, error) {
	ipAddr, err := net.ResolveIPAddr("ip", config.Multicast)
	if err != nil {
		return nil, err
	}
	ifcs := network.GetNetInterfaces()
	if len(ifcs) == 0 {
		return nil, errors.New("未获取到网卡信息")
	}
	return &Discovery{
		config:    config,
		log:       log,
		ifcs:      ifcs,
		groupAddr: &net.UDPAddr{IP: ipAddr.IP},
		msgHandle: settingService.DiscoveryHandle,

		msgChan: make(chan msgChan, 10),
	}, nil

}

func (s *Discovery) Start(ctx context.Context) error {
	listener, err := net.ListenPacket("udp4", fmt.Sprintf("0.0.0.0:%d", s.config.Port))
	if err != nil {
		return err
	}
	s.packetConn = ipv4.NewPacketConn(listener)

	// 加入组播
	//group := net.IPv4(239, 255, 255, 250)
	for i := range s.ifcs {
		if err = s.packetConn.JoinGroup(s.ifcs[i].Ifc, s.groupAddr); err != nil {
			return err
		}
	}
	go s.readLoop(ctx)
	go s.writeLoop(ctx)
	s.log.Info("discovery start...")
	return nil
}

func (s *Discovery) readLoop(ctx context.Context) {
	for {
		// 接收数据
		// 创建一个缓冲区(切片)用于接收数据
		buffer := make([]byte, 1024)
		n, cm, srcAddr, _err := s.packetConn.ReadFrom(buffer)
		if _err != nil {
			s.log.Error("packConnector.ReadFrom error", zap.Error(_err))
			return
		}
		// 处理消息，目前只有一个发现信息，返回设备信息即可
		s.msgChan <- msgChan{
			msg: buffer[:n],
			cm:  cm,
			src: srcAddr,
		}
		//if s.msgHandle != nil {
		//	go func() {
		//		res, _err := s.msgHandle(buffer[:n])
		//		if res != nil && _err == nil {
		//			if n, _err = s.packetConn.WriteTo(res, cm, srcAddr); _err != nil {
		//				s.log.Error("packetConn.WriteTo error", zap.String("srcAddr", srcAddr.String()), zap.Error(_err))
		//			}
		//		}
		//	}()
		//}

	}
}
func (s *Discovery) writeLoop(ctx context.Context) {
	for msg := range s.msgChan {
		//go func(msg msgChan) {
		res, _err := s.msgHandle(msg.msg, msg.src)
		if res != nil && _err == nil {
			if _, _err = s.packetConn.WriteTo(res, msg.cm, msg.src); _err != nil {
				s.log.Error("packetConn.WriteTo error", zap.String("srcAddr", msg.src.String()), zap.Error(_err))
				return
			}
		}
		//}(msg)
	}
}

func (s *Discovery) Stop(ctx context.Context) error {
	// 离开组播
	for i := range s.ifcs {
		if err := s.packetConn.LeaveGroup(s.ifcs[i].Ifc, s.groupAddr); err != nil {
			s.log.Error("packetConn.LeaveGroup error", zap.Error(err))
		}
	}
	if _err := s.packetConn.Close(); _err != nil {
		s.log.Error("packetConn.Close error", zap.Error(_err))
	}
	s.log.Info("discovery exit")
	return nil
}
